﻿using System;
using System.Collections.Generic;

namespace TLang
{
    using Values;
    using Parsers;
    using Ast;

    public class TypeChecker
    {

        public static TypeChecker self;
        public String file;
        public ISet<FunType> uncalled = new HashSet<FunType>();
        public ISet<FunType> callStack = new HashSet<FunType>();


        public TypeChecker(String file)
        {
            this.file = file;
        }


        public Value typecheck(string text, String file)
        {
            Node program;
            try
            {
                program = Parser.Parse(text, file);
            }
            catch (ParserException e)
            {
                Util.Abort("parsing error: " + e);
                return null;
            }
            Scope s = Scope.BuildInitTypeScope();
            Value ret = program.Typecheck(s);

            while (!uncalled.IsEmpty())
            {
                List<FunType> toRemoves = new List<FunType>(uncalled);
                foreach (FunType ft in toRemoves)
                {
                    invokeUncalled(ft, s);
                }
                uncalled.RemoveAll(toRemoves);
            }

            return ret;
        }


        public void invokeUncalled(FunType fun, Scope s)
        {
            Scope funScope = new Scope(fun.env);
            if (fun.properties != null)
            {
                Declare.mergeType(fun.properties, funScope);
            }

            TypeChecker.self.callStack.Add(fun);
            Value actual = fun.fun.body.Typecheck(funScope);
            TypeChecker.self.callStack.Remove(fun);

            Object retNode = fun.properties.LookupPropertyLocal(Constants.RETURN_ARROW, "type");

            if (retNode == null || !(retNode is Node)) {
                Util.Abort("illegal return type: " + retNode);
                return;
            }

            Value expected = ((Node)retNode).Typecheck(funScope);
            if (!Type.subtype(actual, expected, true))
            {
                Util.Abort(fun.fun, "type error in return value, expected: " + expected + ", actual: " + actual);
            }
        }


        public static void main(String[] args)
        {
            TypeChecker tc = new TypeChecker(args[0]);
            TypeChecker.self = tc;
            Value result = tc.typecheck(null, args[0]);
            Util.Msg(result.ToString());
        }
    }
}
